package com.my.exception;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.my.core.api.R;
import com.my.core.api.ResultCode;
import com.my.event.publisher.ErrorLogPublisher;
import com.my.h2.logs.LogError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.WebRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @author timo 2021/2/25
 */
public class CustomErrorAttributes extends DefaultErrorAttributes {
    private static final Logger log = LoggerFactory.getLogger(CustomErrorAttributes.class);

    public CustomErrorAttributes() {
    }

    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        //当请求（如访问 /index ）发生异常时，容器会转发到错误处理路径（如Spring Boot默认的 /error ），
        //此时该属性会保存原始请求的URI（ /index ）。
        String requestUri = (String)this.getAttr(webRequest, "javax.servlet.error.request_uri");
        Integer status = (Integer)this.getAttr(webRequest, "javax.servlet.error.status_code");
        Throwable error = this.getError(webRequest);
        R result;
        if (error == null) {
            log.error("URL:{} error status:{}", requestUri, status);
            if(status==404){
                result = R.fail(ResultCode.NOT_FOUND, "[HttpStatus]:" + status);
            }else{
                result = R.fail(ResultCode.FAILURE, "系统未知异常[HttpStatus]:" + status);
            }
        } else {
            log.error(String.format("URL:%s error status:%d", requestUri, status), error);
            result = R.fail(status, error.getMessage());
        }
        LogError logError = new LogError();
        logError.setStatus(status);
        logError.setResult(JSON.toJSONString(result));
        // 补充错误日志参数
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();
        // 获取请求信息
        //在错误处理流程中，当前请求实际是转发到错误处理端点（如 /error ）的请求，
        //因此 requestUrl 会是错误处理端点的URL（例如 http://localhost:8080/error ）。
        String requestUrl = request.getRequestURL().toString();
        String method = request.getMethod();
        String remoteAddr = request.getRemoteAddr();

        // 获取请求参数
        StringBuilder params = new StringBuilder();
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String paramName = parameterNames.nextElement();
            params.append(paramName).append("=")
                    .append(request.getParameter(paramName)).append("&");
        }
        long beginTime = System.currentTimeMillis();
        long time = System.currentTimeMillis() - beginTime;

        // 获取请求头
        Map<String, String> requestHeaders = new HashMap<>();
        Enumeration<String> reqHeaderNames = request.getHeaderNames();
        while (reqHeaderNames.hasMoreElements()) {
            String headerName = reqHeaderNames.nextElement();
            requestHeaders.put(headerName, request.getHeader(headerName));
        }
        // 获取响应头
        Map<String, String> responseHeaders = new HashMap<>();
        if (response != null) {
            Collection<String> respHeaderNames = response.getHeaderNames();
            for (String headerName : respHeaderNames) {
                responseHeaders.put(headerName, response.getHeader(headerName));
            }
        }

        logError.setParams(params.length() > 0 ? params.substring(0, params.length() - 1) : "");
        logError.setRequestHeaders(JSON.toJSONString(requestHeaders));
        logError.setResponseHeaders(JSON.toJSONString(responseHeaders));
        logError.setRequestUrl(requestUri);
        logError.setCreateTime(new Date());

        ErrorLogPublisher.publishEvent(error, requestUri,logError);
        return  BeanUtil.beanToMap(result);
    }

    @Nullable
    private <T> T getAttr(WebRequest webRequest, String name) {
        return (T) webRequest.getAttribute(name, 0);
    }
}
